Il est souvent nécessaire d’utiliser des techniques de visualisation à toutes les étapes d’une étude statistique. Un des avantages de R est qu’il est relativement simple de mettre en oeuvre tout les types de graphes généralement utilisés. Dans cette fiche, nous présentons tout d’abord les fonctions classiques qui permettent de tracer des figures. Nous proposons ensuite une introduction aux graphes ggplot qui sont de plus en plus utilisés pour faire de la visualisation.

1. Fonctions graphiques conventionnelles

Pour commencer il est intéressant d’examiner quelques exemples de représentations graphiques construits avec R. On peut les obtenir à l’aide de la fonction demo.

demo(graphics)

La fonction plot

C’est une fonction générique que l’on peut utiliser pour représenter différents types de données. L’utilisation standard consiste à visualiser une variable y en fonction d’une variable x. On peut par exemple obtenir le graphe de la fonction \(x\mapsto \sin(2\pi x)\) sur \([0,1]\), à l’aide de

x <- seq(-2*pi,2*pi,by=0.05)
y <- sin(x)
plot(x,y) #points (par défaut)

plot(x,y,type="l") #représentation sous forme de ligne

Nous proposons des exemples de représentations de variables quantitatives et qualitatives à l’aide du jeu de données ozone.txt que l’on importe avec

ozone <- read.table("ozone.txt")
summary(ozone)
     maxO3              T9             T12             T15       
 Min.   : 42.00   Min.   :11.30   Min.   :14.00   Min.   :14.90  
 1st Qu.: 70.75   1st Qu.:16.20   1st Qu.:18.60   1st Qu.:19.27  
 Median : 81.50   Median :17.80   Median :20.55   Median :22.05  
 Mean   : 90.30   Mean   :18.36   Mean   :21.53   Mean   :22.63  
 3rd Qu.:106.00   3rd Qu.:19.93   3rd Qu.:23.55   3rd Qu.:25.40  
 Max.   :166.00   Max.   :27.00   Max.   :33.50   Max.   :35.50  
      Ne9             Ne12            Ne15           Vx9         
 Min.   :0.000   Min.   :0.000   Min.   :0.00   Min.   :-7.8785  
 1st Qu.:3.000   1st Qu.:4.000   1st Qu.:3.00   1st Qu.:-3.2765  
 Median :6.000   Median :5.000   Median :5.00   Median :-0.8660  
 Mean   :4.929   Mean   :5.018   Mean   :4.83   Mean   :-1.2143  
 3rd Qu.:7.000   3rd Qu.:7.000   3rd Qu.:7.00   3rd Qu.: 0.6946  
 Max.   :8.000   Max.   :8.000   Max.   :8.00   Max.   : 5.1962  
      Vx12             Vx15            maxO3v          vent   
 Min.   :-7.878   Min.   :-9.000   Min.   : 42.00   Est  :10  
 1st Qu.:-3.565   1st Qu.:-3.939   1st Qu.: 71.00   Nord :31  
 Median :-1.879   Median :-1.550   Median : 82.50   Ouest:50  
 Mean   :-1.611   Mean   :-1.691   Mean   : 90.57   Sud  :21  
 3rd Qu.: 0.000   3rd Qu.: 0.000   3rd Qu.:106.00             
 Max.   : 6.578   Max.   : 5.000   Max.   :166.00             
   pluie   
 Pluie:43  
 Sec  :69  
           
           
           
           

On visualise tout d’abord 2 variables quantitatives à l’aide d’un nuage de points : la concentration en ozone maximale maxO3 en fonction de la température à 12h T12.

plot(ozone[,"T12"],ozone[,"maxO3"])

Comme les deux variables appartiennent au même jeu de données, on peut obtenir la même représentation à l’aide d’une sytaxe plus claire qui ajoutent automatiquement les noms des variables sur les axes :

plot(maxO3~T12,data=ozone)

Une autre façon de faire (moins naturelle) :

plot(ozone[,"T12"],ozone[,"maxO3"],xlab="T12",ylab="maxO3")

Il existe des fonctions spécifiques pour chaque type de graphs, par exemple histogram, barplot et boxplot :

hist(ozone$maxO3,main="Histogram")

barplot(table(ozone$vent)/nrow(ozone),col="blue")

boxplot(maxO3~vent,data=ozone)

Graphes interactifs avec rAmCharts

On peut utiliser ce package pour obtenir des graphes dynamiques. L’utilisation est relativement simple, il suffit d’ajouter le prefixe am devant le nom de la fonction :

library(rAmCharts)
amHist(ozone$maxO3)

amPlot(ozone,col=c("T9","T12"))

amBoxplot(maxO3~vent,data=ozone)

Exercice 1

  • Tracer la fonction sinus entre \(0\) et \(2\pi\).
  • A l’aide de la fonction title ajouter le titre Représentation de la fonction sinus.

Exercice 2

  • Tracer la densité de la loi normale centrée réduite entre \(-4\) et 4 (utiliser dnorm).
  • Ajouter une ligne verticale (en tirets) qui passe par \(x=0\) (utiliser abline avec lty=2).
  • Sur le même graphe, ajouter les densités de loi la de Student à 5 et 30 degrés de liberté (utiliser dt). On utilisera la fonction lines et des couleurs différentes pour chaque densité.
  • Ajouter une légende qui permet de repérer chaque densité (fonction legend).

Exercice 3

  • Importer la série taches_solaires.csv qui donne, date par date, un nombre de taches solaires observés.

  • A l’aide de la fonction cut_interval du tidyverse créer un facteur qui sépare l’intervalle d’années d’observation en 8 intervalles de tailles à peu près égales. On appellera periode ce facteur.

  • Utiliser les levels suivants pour le facteur periode.

couleurs <- c("yellow", "magenta", "orange", "cyan", "grey", "red", "green", "blue")
  • Expliquer la sortie de la fonction
coordx <- seq(along=taches[,1])

On crée une séquence avec un pas de 1 de longueur égale à la dimension de taches[,1].

  • Visualiser la série du nombre de taches en utilisant une couleur différente pour chaque période.

Exercice 4

On reprend le jeu de données sur l’ozone. A l’aide de la fonction layout séparer la fenêtre graphique en deux lignes avec

  • un graphe sur la première ligne (nuage de points maxO3 vs T12)
  • 2 graphes sur la deuxième colonne (histogramme de T12 et boxplot de maxO3).

2. Le package ggplot2

Ce package propose de définir des graphes sur R en utilisant une grammaire des graphiques (tout comme dplyr pour manipuler les données). On peut trouver de la documentation sur ce package ici. Nous considérons un sous échantillon du jeu de données diamonds du package ggplot2 (qui se trouve dans le tidyverse).

library(tidyverse)
set.seed(1234)
diamonds2 <- diamonds[sample(nrow(diamonds),5000),] 
summary(diamonds2)
     carat               cut       color       clarity    
 Min.   :0.2000   Fair     : 158   D: 640   SI1    :1189  
 1st Qu.:0.4000   Good     : 455   E: 916   VS2    :1157  
 Median :0.7000   Very Good:1094   F: 900   SI2    : 876  
 Mean   :0.7969   Premium  :1280   G:1018   VS1    : 738  
 3rd Qu.:1.0400   Ideal    :2013   H: 775   VVS2   : 470  
 Max.   :4.1300                    I: 481   VVS1   : 326  
                                   J: 270   (Other): 244  
     depth           table           price             x         
 Min.   :43.00   Min.   :49.00   Min.   :  365   Min.   : 0.000  
 1st Qu.:61.10   1st Qu.:56.00   1st Qu.:  945   1st Qu.: 4.720  
 Median :61.80   Median :57.00   Median : 2376   Median : 5.690  
 Mean   :61.76   Mean   :57.43   Mean   : 3917   Mean   : 5.728  
 3rd Qu.:62.50   3rd Qu.:59.00   3rd Qu.: 5294   3rd Qu.: 6.530  
 Max.   :71.60   Max.   :95.00   Max.   :18757   Max.   :10.000  
                                                                 
       y               z        
 Min.   :3.720   Min.   :0.000  
 1st Qu.:4.720   1st Qu.:2.920  
 Median :5.700   Median :3.520  
 Mean   :5.731   Mean   :3.538  
 3rd Qu.:6.520   3rd Qu.:4.030  
 Max.   :9.850   Max.   :6.430  
                                
help(diamonds)

Pour un jeu de données considéré, un graphe ggplot est défini à partir de couches. Il faut a minima spécifier :

  • les données
  • les variables que l’on souhaite représenter
  • le type de représentation (nuage de points, boxplot…).

Il existe un verbe pour définir chacune de ces couches :

  • ggplot pour les données
  • aes (aesthetics) pour les variables
  • geom_ pour le type de représentation.

On peut obtenir le nuage de points carat vs price avec la fonction plot :

plot(price~carat,data=diamonds2)

Avec ggplot, on va faire

ggplot(diamonds2) #rien

ggplot(diamonds2)+aes(x=carat,y=price) #rien

ggplot(diamonds2)+aes(x=carat,y=price)+geom_point() #bon

Exercice 5

  • Tracer l’histogramme de la variable carat (utiliser geom_histogram).
  • Tracer l’histogramme de la variable carat avec 10 classes (help(geom_histogram)).
  • Tracer le diagramme à batons de la variable cut (utiliser geom_bar).

2.1 Grammaire ggplot

La syntaxe ggplot se construit à partir d’éléments indépendants qui définissent la grammaire de ggplot. Les principaux verbes sont :

  • Data (ggplot) : les données au format dataframe ou tibble
  • Aesthetics (aes) : pour sépecifier les variables à représenter dans le graphe.
  • Geometrics (geom_…) : le type de graphe.
  • Statistics (stat_…) : utile pour spécifier des transformations des données nécessaires pour obtenir le graphe.
  • Scales (scale_…) : pour controler les paramètres permettant d’affiner le graphe (changement de couleurs, paramètres des axes…). Tous ces éléments sont reliés avec le symbole +.

Data et aesthetics

Ces deux éléments renseignent les données et les variables à représenter. Par exemple, pour le nuage de points price vs carat on fera

ggplot(diamonds2)+aes(x=carat,y=price)

On peut aussi utiliser les arguments color, size, fill dans la fonction aes lorsque des couleurs ou des tailles sont définies par des variables du jeu de données. Par exemple

ggplot(diamonds2)+aes(x=carat,y=price,color=cut)

Geometrics

Utile pour décrire le type de représentations. Pour un nuage de points, on utilisera par exemple geom_point :

ggplot(diamonds2)+aes(x=carat,y=price,color=cut)+geom_point()

On observe que ggplot ajoute la légende automatiquement. Voici quelques exemples de geometrics :

Geom Description Aesthetics
geom_point() nuage de points x, y, shape, fill
geom_line() Ligne (ordonnée selon x) x, y, linetype
geom_abline() Ligne slope, intercept
geom_path() Ligne (ordonnée par l’index) x, y, linetype
geom_text() Texte x, y, label, hjust, vjust
geom_rect() Rectangle xmin, xmax, ymin, ymax, fill, linetype
geom_polygon() Polygone x, y, fill, linetype
geom_segment() Segment x, y, fill, linetype
geom_bar() Diaggramme en barres x, fill, linetype, weight
geom_histogram() Histogramme x, fill, linetype, weight
geom_boxplot() Boxplot x, y, fill, weight
geom_density() Densité x, y, fill, linetype
geom_contour() Lignes de contour x, y, fill, linetype
geom_smooth() Lisseur (linéaire ou non linéaire) x, y, fill, linetype
Tous color, size, group

Exercice 6

  • Tracer le diagramme en barres de la variable cut (avec des barres bleus).
  • Tracer le diagramme en barres de la variable cut (avec une couleur pour chaque modalité de cut).

Statistics (partie optionnelle)

Certains graphes nécessitent des calculs d’indicateurs statistiques, comme par exemple le diagamme en barres ou l’histogramme où il faut calculer des hauteurs. Les transformations simples peuvent se faire rapidement, on peut par exemple tracer la fonction sinus avec

D <- data.frame(X=seq(-2*pi,2*pi,by=0.01))
ggplot(D)+aes(x=X,y=sin(X))+geom_line()

La transformation est précisée dans la fonction aes. Pour des transformations plus complexes, nous devons utiliser des statistics. Une fonction stat permet de définir des nouvelles variables à partir du jeu de données initial, il est ensuite possible de représenter ces nouvelles variables. Par exemple, la fonction stat_bin, qui est utilisée par défaut pour construire des histogrammes, produit les variables suivantes :

  • count, le nombre d’observations dans chaque classes.
  • density, la valeur de la densité des observations dans chaque classe (fréquance divisée par largeur de la classe).
  • x, le centre de la classe.

Par défaut geom_histogram représente sur l’axe \(y\) le nombre d’observations dans chaque classe (la variable count).

ggplot(diamonds)+aes(x=price)+geom_histogram(bins=40)

Si on souhaite visualiser la densité, il faudra utiliser

ggplot(diamonds)+aes(x=price,y=..density..)+geom_histogram(bins=40)

Il est possible d’utiliser les fonctions stat_ à la place des geom_ pour certaines représentations. Chaque fonction stat_ possède par défaut un geom_ et réciproquement. Par exemple,

ggplot(diamonds2)+aes(x=carat,y=price)+geom_smooth(method="loess")

ggplot(diamonds2)+aes(x=carat,y=price)+stat_smooth(method="loess")

produisent le même graphe. On peut changer certaines options graphiques dans les fonctions stat_ en utilisant l’option geom :

ggplot(diamonds2)+aes(x=carat,y=price)+stat_smooth(method="loess",geom="point")

Voici quelques exemple de fonctions stat_

Stat Description Paramètres
stat_identity() aucune transformation
stat_bin() Count binwidth, origin
stat_density() Density adjust, kernel
stat_smooth() Smoother method, se
stat_boxplot() Boxplot coef

stat et geom ne sont pas toujours simples à combiner. Nous recommandons d’utiliser geom lorsqu’on débute avec ggplot.

Exercice 7 (optional)

On considère une variable qualitative \(X\) dont la loi est donnée par \[P(X=red)=0.3,\ P(X=blue)=0.2,\ P(X=green)=0.4,\ P(X=black)=0.1\] Tracer le digramme en barres associé à cette distribution.

Scales

Les échelles (scales) controlent certaines options pour les variables utilisées dans aes (changement de couleurs, de tailles…). On utilise généralement ce verbe à la dernière étape de construction du graphe. La syntaxe est définie comme suit :

  • début scale_.
  • ajout de l’aesthetics que l’on souhaite modifier (color, fill, x_).
  • fin : nom de l’échelle (manual, identity…)

Par exemple,

ggplot(diamonds2)+aes(x=carat,y=price,color=cut)+geom_point()+
scale_color_manual(values=c("Fair"="black","Good"="yellow",
"Very Good"="blue","Premium"="red","Ideal"="green"))

Voici quelques exemples des principales échelles :

aes Discret Continu
Couleur (color et fill) brewer gradient
- grey gradient2
- hue gradientn
- identity
- manual
Position (x et y) discrete continous
- date
Forme shape
- identity
- manual
Taille identity size
- manual

Nous présentons maintenant quelques exemples :

  • Couleur dans un diagramme en barres
p1 <- ggplot(diamonds2)+aes(x=cut)+geom_bar(aes(fill=cut))
p1

On change la couleur en utilisant la palette Purples :

p1+scale_fill_brewer(palette="Purples")

  • Gradient de couleurs pour un nuage de points :
p2 <- ggplot(diamonds2)+aes(x=carat,y=price)+geom_point(aes(color=depth))
p2

On change le gradient de couleur

p2+scale_color_gradient(low="red",high="yellow")

  • Modification sur les axes
p2+scale_x_continuous(breaks=seq(0.5,3,by=0.5))+scale_y_continuous(name="prix")+scale_color_gradient("Profondeur")

Group et facets

ggplot permet de faire des représentations pour des groupes d’individus. Il est possible de procéder de deux façons différentes :

  • représenter des sous groupes sur le même graphe, on utilise l’option group dans aes ;
  • représenter des sous groupes sur des graphes différents, on utilise le verbe facets.

Représentons ici (sur le même graphe) le lisseur price vs carat pour chaque modalité de cut

ggplot(diamonds2)+aes(x=carat,y=price,group=cut,color=cut)+geom_smooth(method="loess")

Pour obtenir cette représentation sur plusieurs fenêtres, on utilise

ggplot(diamonds2)+aes(x=carat,y=price)+geom_smooth(method="loess")+facet_wrap(~cut)

ggplot(diamonds2)+aes(x=carat,y=price)+geom_smooth(method="loess")+facet_wrap(~cut,nrow=1)

facet_grid et facet_wrap font des choses proches mais divisent la fenêtre de façon différente :

ggplot(diamonds2)+aes(x=carat,y=price)+geom_point()+geom_smooth(method="lm")+facet_grid(color~cut)

ggplot(diamonds2)+aes(x=carat,y=price)+geom_point()+geom_smooth(method="lm")+facet_wrap(color~cut)

2.2 Compléments

La syntaxe ggplot est définie selon le schéma :

ggplot()+aes()+geom_()+scale_()

Elle est très flexible, on peut par exemple spécifier les aes dans les verbes ggplot ou geom_ :

ggplot(diamonds2)+aes(x=carat,y=price)+geom_point()

ggplot(diamonds2,aes(x=carat,y=price))+geom_point()

ggplot(diamonds2)+geom_point(aes(x=carat,y=price))

Ceci peut se révéler très utile lorsqu’on utilise des aes différents dans les geom_.

On peut aussi construire un graphe à l’aide de différents jeux de données :

X <- seq(-2*pi,2*pi,by=0.001)
Y1 <- cos(X)
Y2 <- sin(X)
donnees1 <- data.frame(X,Y1)
donnees2 <- data.frame(X,Y2)
ggplot(donnees1)+geom_line(aes(x=X,y=Y1))+
geom_line(data=donnees2,aes(x=X,y=Y2),color="red")

Il existe d’autres fonctions ggplot :

  • ggtitle pour ajouter un titre.
  • ggsave pour sauver un graphe.
  • theme_ pour changer le theme du graphe.
p <- ggplot(diamonds2)+aes(x=carat,y=price,color=cut)+geom_point()
p+theme_bw()

p+theme_classic()

p+theme_grey()

p+theme_bw()

Exercice 8

  1. Tracer les fonctions sinus et cosinus. On utilisera tout d’abord deux jeux de données : un pour le sinus, l’autre pour le cosinus.

  2. Faire la même chose avec un jeu de données et deux geom_line.

  3. Faire la même chose avec un jeu de données et un seul geom_line. On pourra utiliser la fonction gather du tidyverse.

  4. Tracer les deux fonctions sur deux fenêtres graphiques (utiliser facet_wrap).

  5. Faire la même chose avec grid.arrange du package gridExtra.

Exercice 9

On considère les données mtcars

data(mtcars)
summary(mtcars)
      mpg             cyl             disp             hp       
 Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  
 1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  
 Median :19.20   Median :6.000   Median :196.3   Median :123.0  
 Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  
 3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  
 Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  
      drat             wt             qsec             vs        
 Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
 1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
 Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
 Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
 3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
 Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
       am              gear            carb      
 Min.   :0.0000   Min.   :3.000   Min.   :1.000  
 1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
 Median :0.0000   Median :4.000   Median :2.000  
 Mean   :0.4062   Mean   :3.688   Mean   :2.812  
 3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
 Max.   :1.0000   Max.   :5.000   Max.   :8.000  
  1. Tracer l’histograme de mpg (on fera varier le nombre de classes).

2.Tracer l’histogramme de la densité.

  1. Tracer le diagramme en barres de cyl.

  2. Tracer le nuage de points disp vs mpg en utilisant une couleur différente pour chaque valeur de cyl.

  3. Ajouter le lisseur linéaire sur le graphe.

Exercice 10

  1. Générer un échantillon \((x_i,y_i),i=1,\dots,100\) selon le modèle linéaire \[Y_i=3+X_i+\varepsilon_i\]\(X_i\) sont i.i.d. de loi uniforme sur \([0,1]\) et \(\varepsilon_i\) sont i.i.d. de loi gaussienne \(N(0,0.2^2)\) (utiliser runif et rnorm).

  2. Tracer le nuage de points Y vs X et ajouter le lisseur linéaire.

  3. Représenter les résidus : on ajoutera une ligne verticale entre chaque point et la droite de lissage (utiliser geom_segment).

Challenge (option)

On considère les données diamonds.

  1. Tracer les graphes suivants (utiliser coord_flip pour le second).

  1. Ajouter sur le troisième graphe les quartiles de la variable carat pour chaque valeur de cut. On utilisera une ligne verticale.

  2. En déduire le graphe suivant (on utilisera le package ggstance).

Exercice 11

On considère les données tennis sur les tournois du grand chelem présentés dans la fiche précédente.

FrenchOpen_men_2013 <- read_csv("FrenchOpen-men-2013.csv")
RG2013 <- FrenchOpen_men_2013
WIMB2013 <- read_csv("Wimbledon-men-2013.csv")
RG_WIMB2013 <- bind_rows("RG"=RG2013,"WIMB"=WIMB2013,.id="Tournament")
  1. Tracer deux histogrammes (un pour Roland Garros, l’autre pour wimbledon) pour visualiser la distribution du nombre d’aces par match.

  2. Comparer les nombres d’aces par match entre Roland Garros et Wimbledon à l’aide d’un boxplot.

  3. Comparer les nombres de points joués au filet (NPA.1+NPA.2) par match entre Roland Garros and Wimbledon avec un boxplot.

  4. Pour le tournoi de Roland Garros, étudier à l’aide d’un boxplot l’influence du premier service sur le résultat du match.

  5. faire la même chose pour Wimbledon.

  6. Comparer les pourcentages de premier service des vaiqueurs de match entre les tournois de Rolang Garros et Wimbledon.

LS0tCnRpdGxlOiAiVmlzdWFsaXNhdGlvbiBkZXMgZG9ubsOpZXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCmJvZHl7IC8qIE5vcm1hbCAgKi8KICAgICAgZm9udC1zaXplOiAxMnB4OwogIH0KdGQgeyAgLyogVGFibGUgICovCiAgZm9udC1zaXplOiAxMnB4Owp9CmgxLnRpdGxlIHsKICBmb250LXNpemU6IDQ4cHg7CiAgY29sb3I6IERhcmtSZWQ7Cn0KaDEgeyAvKiBIZWFkZXIgMSAqLwogIGZvbnQtc2l6ZTogMzVweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KaDIgeyAvKiBIZWFkZXIgMiAqLwogICAgZm9udC1zaXplOiAyOHB4OwogIGNvbG9yOiBEYXJrQmx1ZTsKfQpoMyB7IC8qIEhlYWRlciAzICovCiAgZm9udC1zaXplOiAyNHB4OwogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKfQpjb2RlLnJ7IC8qIENvZGUgYmxvY2sgKi8KICAgIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgeyAvKiBDb2RlIGJsb2NrIC0gZGV0ZXJtaW5lcyBjb2RlIHNwYWNpbmcgYmV0d2VlbiBsaW5lcyAqLwogICAgZm9udC1zaXplOiAxNHB4Owp9Cjwvc3R5bGU+CgpJbCBlc3Qgc291dmVudCBuw6ljZXNzYWlyZSBkJ3V0aWxpc2VyIGRlcyB0ZWNobmlxdWVzIGRlIHZpc3VhbGlzYXRpb24gw6AgdG91dGVzIGxlcyDDqXRhcGVzIGQndW5lIMOpdHVkZSBzdGF0aXN0aXF1ZS4gVW4gZGVzIGF2YW50YWdlcyBkZSAqKlIqKiBlc3QgcXUnaWwgZXN0IHJlbGF0aXZlbWVudCBzaW1wbGUgZGUgbWV0dHJlIGVuIG9ldXZyZSB0b3V0IGxlcyB0eXBlcyBkZSBncmFwaGVzIGfDqW7DqXJhbGVtZW50IHV0aWxpc8Opcy4gRGFucyBjZXR0ZSBmaWNoZSwgbm91cyBwcsOpc2VudG9ucyB0b3V0IGQnYWJvcmQgbGVzIGZvbmN0aW9ucyBjbGFzc2lxdWVzIHF1aSBwZXJtZXR0ZW50IGRlIHRyYWNlciBkZXMgZmlndXJlcy4gTm91cyBwcm9wb3NvbnMgZW5zdWl0ZSB1bmUgaW50cm9kdWN0aW9uIGF1eCBncmFwaGVzICAqKmdncGxvdCoqIHF1aSBzb250IGRlIHBsdXMgZW4gcGx1cyB1dGlsaXPDqXMgcG91ciBmYWlyZSBkZSBsYSB2aXN1YWxpc2F0aW9uLgoKCiMgMS4gRm9uY3Rpb25zIGdyYXBoaXF1ZXMgY29udmVudGlvbm5lbGxlcwoKUG91ciBjb21tZW5jZXIgaWwgZXN0IGludMOpcmVzc2FudCBkJ2V4YW1pbmVyIHF1ZWxxdWVzIGV4ZW1wbGVzIGRlIHJlcHLDqXNlbnRhdGlvbnMgZ3JhcGhpcXVlcyBjb25zdHJ1aXRzIGF2ZWMgKipSKiouIE9uIHBldXQgbGVzIG9idGVuaXIgw6AgbCdhaWRlIGRlIGxhIGZvbmN0aW9uICoqZGVtbyoqLgoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KZGVtbyhncmFwaGljcykKYGBgCgojIyBMYSBmb25jdGlvbiAqKnBsb3QqKgoKQydlc3QgdW5lICoqZm9uY3Rpb24gZ8OpbsOpcmlxdWUqKiBxdWUgbCdvbiBwZXV0IHV0aWxpc2VyIHBvdXIgcmVwcsOpc2VudGVyIGRpZmbDqXJlbnRzIHR5cGVzIGRlIGRvbm7DqWVzLiBMJ3V0aWxpc2F0aW9uIHN0YW5kYXJkIGNvbnNpc3RlIMOgIHZpc3VhbGlzZXIgdW5lIHZhcmlhYmxlICp5KiBlbiBmb25jdGlvbiBkJ3VuZSB2YXJpYWJsZSAqeCouIE9uIHBldXQgcGFyIGV4ZW1wbGUgb2J0ZW5pciBsZSBncmFwaGUgZGUgbGEgZm9uY3Rpb24gJHhcbWFwc3RvIFxzaW4oMlxwaSB4KSQgc3VyICRbMCwxXSQsIMOgIGwnYWlkZSBkZQoKYGBge3J9CnggPC0gc2VxKC0yKnBpLDIqcGksYnk9MC4wNSkKeSA8LSBzaW4oeCkKcGxvdCh4LHkpICNwb2ludHMgKHBhciBkw6lmYXV0KQpwbG90KHgseSx0eXBlPSJsIikgI3JlcHLDqXNlbnRhdGlvbiBzb3VzIGZvcm1lIGRlIGxpZ25lCmBgYAoKTm91cyBwcm9wb3NvbnMgZGVzIGV4ZW1wbGVzIGRlIHJlcHLDqXNlbnRhdGlvbnMgZGUgdmFyaWFibGVzIHF1YW50aXRhdGl2ZXMgZXQgcXVhbGl0YXRpdmVzIMOgIGwnYWlkZSBkdSBqZXUgZGUgZG9ubsOpZXMgKipvem9uZS50eHQqKiBxdWUgbCdvbiBpbXBvcnRlIGF2ZWMKCmBgYHtyfQpvem9uZSA8LSByZWFkLnRhYmxlKCJvem9uZS50eHQiKQpzdW1tYXJ5KG96b25lKQpgYGAKCk9uIHZpc3VhbGlzZSB0b3V0IGQnYWJvcmQgMiB2YXJpYWJsZXMgcXVhbnRpdGF0aXZlcyDDoCBsJ2FpZGUgZCd1biBudWFnZSBkZSBwb2ludHMgOiBsYSBjb25jZW50cmF0aW9uIGVuIG96b25lIG1heGltYWxlICoqbWF4TzMqKiBlbiBmb25jdGlvbiBkZSBsYSB0ZW1ww6lyYXR1cmUgw6AgMTJoICoqVDEyKiouCmBgYHtyfQpwbG90KG96b25lWywiVDEyIl0sb3pvbmVbLCJtYXhPMyJdKQpgYGAKCkNvbW1lIGxlcyBkZXV4IHZhcmlhYmxlcyBhcHBhcnRpZW5uZW50IGF1IG3Dqm1lIGpldSBkZSBkb25uw6llcywgb24gcGV1dCBvYnRlbmlyIGxhIG3Dqm1lIHJlcHLDqXNlbnRhdGlvbiDDoCBsJ2FpZGUgZCd1bmUgc3l0YXhlIHBsdXMgY2xhaXJlIHF1aSBham91dGVudCBhdXRvbWF0aXF1ZW1lbnQgbGVzIG5vbXMgZGVzIHZhcmlhYmxlcyBzdXIgbGVzIGF4ZXMgOgoKYGBge3J9CnBsb3QobWF4TzN+VDEyLGRhdGE9b3pvbmUpCmBgYAoKVW5lIGF1dHJlIGZhw6dvbiBkZSBmYWlyZSAobW9pbnMgbmF0dXJlbGxlKSA6CgpgYGB7cn0KcGxvdChvem9uZVssIlQxMiJdLG96b25lWywibWF4TzMiXSx4bGFiPSJUMTIiLHlsYWI9Im1heE8zIikKYGBgCgpJbCBleGlzdGUgZGVzIGZvbmN0aW9ucyBzcMOpY2lmaXF1ZXMgcG91ciBjaGFxdWUgdHlwZSBkZSBncmFwaHMsIHBhciBleGVtcGxlICoqaGlzdG9ncmFtKiosICoqYmFycGxvdCoqIGV0ICoqYm94cGxvdCoqIDoKCmBgYHtyfQpoaXN0KG96b25lJG1heE8zLG1haW49Ikhpc3RvZ3JhbSIpCmJhcnBsb3QodGFibGUob3pvbmUkdmVudCkvbnJvdyhvem9uZSksY29sPSJibHVlIikKYm94cGxvdChtYXhPM352ZW50LGRhdGE9b3pvbmUpCmBgYAoKIyMgR3JhcGhlcyBpbnRlcmFjdGlmcyBhdmVjIHJBbUNoYXJ0cwoKT24gcGV1dCB1dGlsaXNlciBjZSBwYWNrYWdlIHBvdXIgb2J0ZW5pciBkZXMgZ3JhcGhlcyBkeW5hbWlxdWVzLiBMJ3V0aWxpc2F0aW9uIGVzdCByZWxhdGl2ZW1lbnQgc2ltcGxlLCBpbCBzdWZmaXQgZCdham91dGVyIGxlIHByZWZpeGUgKiphbSoqIGRldmFudCBsZSBub20gZGUgbGEgZm9uY3Rpb24gOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShyQW1DaGFydHMpCmFtSGlzdChvem9uZSRtYXhPMykKYW1QbG90KG96b25lLGNvbD1jKCJUOSIsIlQxMiIpKQphbUJveHBsb3QobWF4TzN+dmVudCxkYXRhPW96b25lKQpgYGAKCgojIyMgRXhlcmNpY2UgMQoKKiBUcmFjZXIgbGEgZm9uY3Rpb24gKipzaW51cyoqIGVudHJlICQwJCBldCAkMlxwaSQuCiogQSBsJ2FpZGUgZGUgbGEgZm9uY3Rpb24gKip0aXRsZSoqIGFqb3V0ZXIgbGUgdGl0cmUgICoqUmVwcsOpc2VudGF0aW9uIGRlIGxhIGZvbmN0aW9uIHNpbnVzKiouCgoKIyMjIEV4ZXJjaWNlIDIKCiogVHJhY2VyIGxhIGRlbnNpdMOpIGRlIGxhIGxvaSBub3JtYWxlIGNlbnRyw6llIHLDqWR1aXRlIGVudHJlICQtNCQgZXQgNCAodXRpbGlzZXIgKipkbm9ybSoqKS4KKiBBam91dGVyIHVuZSBsaWduZSB2ZXJ0aWNhbGUgKGVuIHRpcmV0cykgcXVpIHBhc3NlIHBhciAkeD0wJCAodXRpbGlzZXIgKiphYmxpbmUqKiBhdmVjICoqbHR5PTIqKikuCiogU3VyIGxlIG3Dqm1lIGdyYXBoZSwgYWpvdXRlciBsZXMgZGVuc2l0w6lzIGRlIGxvaSBsYSBkZSBTdHVkZW50IMOgIDUgZXQgMzAgZGVncsOpcyBkZSBsaWJlcnTDqSAodXRpbGlzZXIgKipkdCoqKS4gT24gdXRpbGlzZXJhIGxhIGZvbmN0aW9uICoqbGluZXMqKiBldCBkZXMgY291bGV1cnMgZGlmZsOpcmVudGVzIHBvdXIgY2hhcXVlIGRlbnNpdMOpLgoqIEFqb3V0ZXIgdW5lIGzDqWdlbmRlIHF1aSBwZXJtZXQgZGUgcmVww6lyZXIgY2hhcXVlIGRlbnNpdMOpIChmb25jdGlvbiAqKmxlZ2VuZCoqKS4KCgoKIyMjIEV4ZXJjaWNlIDMKCiogSW1wb3J0ZXIgbGEgc8OpcmllICoqdGFjaGVzX3NvbGFpcmVzLmNzdioqIHF1aSBkb25uZSwgZGF0ZSBwYXIgZGF0ZSwgdW4gbm9tYnJlIGRlIHRhY2hlcyBzb2xhaXJlcyBvYnNlcnbDqXMuCgoqIEEgbCdhaWRlIGRlIGxhIGZvbmN0aW9uICoqY3V0X2ludGVydmFsKiogZHUgdGlkeXZlcnNlIGNyw6llciB1biBmYWN0ZXVyIHF1aSBzw6lwYXJlIGwnaW50ZXJ2YWxsZSBkJ2FubsOpZXMgZCdvYnNlcnZhdGlvbiBlbiA4IGludGVydmFsbGVzIGRlIHRhaWxsZXMgw6AgcGV1IHByw6hzIMOpZ2FsZXMuIE9uIGFwcGVsbGVyYSAqKnBlcmlvZGUqKiBjZSBmYWN0ZXVyLgoKCiogVXRpbGlzZXIgbGVzIGxldmVscyBzdWl2YW50cyBwb3VyIGxlIGZhY3RldXIgKipwZXJpb2RlKiouCmBgYHtyfQpjb3VsZXVycyA8LSBjKCJ5ZWxsb3ciLCAibWFnZW50YSIsICJvcmFuZ2UiLCAiY3lhbiIsICJncmV5IiwgInJlZCIsICJncmVlbiIsICJibHVlIikKYGBgCgoKKiBFeHBsaXF1ZXIgbGEgc29ydGllIGRlIGxhIGZvbmN0aW9uCgpgYGB7cn0KY29vcmR4IDwtIHNlcShhbG9uZz10YWNoZXNbLDFdKQpgYGAKCk9uIGNyw6llIHVuZSBzw6lxdWVuY2UgYXZlYyB1biBwYXMgZGUgMSBkZSBsb25ndWV1ciDDqWdhbGUgw6AgbGEgZGltZW5zaW9uIGRlICoqdGFjaGVzWywxXSoqLgoKKiBWaXN1YWxpc2VyIGxhIHPDqXJpZSBkdSBub21icmUgZGUgdGFjaGVzIGVuIHV0aWxpc2FudCB1bmUgY291bGV1ciBkaWZmw6lyZW50ZSBwb3VyIGNoYXF1ZSBww6lyaW9kZS4KCgojIyMgRXhlcmNpY2UgNAoKT24gcmVwcmVuZCBsZSBqZXUgZGUgZG9ubsOpZXMgc3VyIGwnb3pvbmUuIEEgbCdhaWRlIGRlIGxhIGZvbmN0aW9uICoqbGF5b3V0Kiogc8OpcGFyZXIgbGEgZmVuw6p0cmUgZ3JhcGhpcXVlIGVuIGRldXggbGlnbmVzIGF2ZWMgCgogICAqIHVuIGdyYXBoZSBzdXIgbGEgcHJlbWnDqHJlIGxpZ25lIChudWFnZSBkZSBwb2ludHMgKiptYXhPMyB2cyBUMTIqKikKICAgKiAyIGdyYXBoZXMgc3VyIGxhIGRldXhpw6htZSBjb2xvbm5lIChoaXN0b2dyYW1tZSBkZSAqKlQxMioqIGV0IGJveHBsb3QgZGUgKiptYXhPMyoqKS4KICAgCgogICAKCiMgMi4gTGUgcGFja2FnZSAqKmdncGxvdDIqKgoKQ2UgcGFja2FnZSBwcm9wb3NlIGRlIGTDqWZpbmlyIGRlcyBncmFwaGVzIHN1ciAqKlIqKiBlbiB1dGlsaXNhbnQgdW5lICoqZ3JhbW1haXJlIGRlcyBncmFwaGlxdWVzKiogKHRvdXQgY29tbWUgKipkcGx5cioqIHBvdXIgbWFuaXB1bGVyIGxlcyBkb25uw6llcykuIE9uIHBldXQgdHJvdXZlciBkZSBsYSBkb2N1bWVudGF0aW9uIHN1ciBjZSBwYWNrYWdlIFtpY2ldKGh0dHA6Ly9nZ3Bsb3QyLm9yZykuIE5vdXMgY29uc2lkw6lyb25zIHVuIHNvdXMgw6ljaGFudGlsbG9uIGR1IGpldSBkZSBkb25uw6llcyAqKmRpYW1vbmRzKiogZHUgcGFja2FnZSAqKmdncGxvdDIqKiAocXVpIHNlIHRyb3V2ZSBkYW5zIGxlICoqdGlkeXZlcnNlKiopLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCnNldC5zZWVkKDEyMzQpCmRpYW1vbmRzMiA8LSBkaWFtb25kc1tzYW1wbGUobnJvdyhkaWFtb25kcyksNTAwMCksXSAKc3VtbWFyeShkaWFtb25kczIpCmhlbHAoZGlhbW9uZHMpCmBgYAoKUG91ciB1biBqZXUgZGUgZG9ubsOpZXMgY29uc2lkw6lyw6ksIHVuIGdyYXBoZSAqKmdncGxvdCoqIGVzdCBkw6lmaW5pIMOgIHBhcnRpciBkZSAqKmNvdWNoZXMqKi4gSWwgZmF1dCBhIG1pbmltYSBzcMOpY2lmaWVyIDoKCiogbGVzIGRvbm7DqWVzCiogbGVzIHZhcmlhYmxlcyBxdWUgbCdvbiBzb3VoYWl0ZSByZXByw6lzZW50ZXIKKiBsZSB0eXBlIGRlIHJlcHLDqXNlbnRhdGlvbiAobnVhZ2UgZGUgcG9pbnRzLCBib3hwbG90Li4uKS4KCklsIGV4aXN0ZSB1biB2ZXJiZSBwb3VyIGTDqWZpbmlyIGNoYWN1bmUgZGUgY2VzIGNvdWNoZXMgOgoKKiAqKmdncGxvdCoqIHBvdXIgbGVzIGRvbm7DqWVzCiogKiphZXMqKiAoYWVzdGhldGljcykgcG91ciBsZXMgdmFyaWFibGVzCiogKipnZW9tXyoqIHBvdXIgbGUgdHlwZSBkZSByZXByw6lzZW50YXRpb24uCgpPbiBwZXV0IG9idGVuaXIgbGUgbnVhZ2UgZGUgcG9pbnRzICoqY2FyYXQgdnMgcHJpY2UqKiAgYXZlYyBsYSBmb25jdGlvbiAqKnBsb3QqKiA6CmBgYHtyfQpwbG90KHByaWNlfmNhcmF0LGRhdGE9ZGlhbW9uZHMyKQpgYGAKCkF2ZWMgKipnZ3Bsb3QqKiwgb24gdmEgZmFpcmUKYGBge3J9CmdncGxvdChkaWFtb25kczIpICNyaWVuCmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpICNyaWVuCmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpK2dlb21fcG9pbnQoKSAjYm9uCmBgYAoKCiMjIyBFeGVyY2ljZSA1CgoqIFRyYWNlciBsJ2hpc3RvZ3JhbW1lIGRlIGxhIHZhcmlhYmxlICoqY2FyYXQqKiAodXRpbGlzZXIgKipnZW9tX2hpc3RvZ3JhbSoqKS4KKiBUcmFjZXIgbCdoaXN0b2dyYW1tZSBkZSBsYSB2YXJpYWJsZSAqKmNhcmF0KiogYXZlYyAxMCBjbGFzc2VzICgqKmhlbHAoZ2VvbV9oaXN0b2dyYW0pKiopLgoqIFRyYWNlciBsZSBkaWFncmFtbWUgw6AgYmF0b25zIGRlIGxhIHZhcmlhYmxlICoqY3V0KiogKHV0aWxpc2VyICoqZ2VvbV9iYXIqKikuCgoKCgojIyAyLjEgR3JhbW1haXJlIGdncGxvdCAKCkxhIHN5bnRheGUgKipnZ3Bsb3QqKiBzZSBjb25zdHJ1aXQgw6AgcGFydGlyIGQnw6lsw6ltZW50cyBpbmTDqXBlbmRhbnRzIHF1aSBkw6lmaW5pc3NlbnQgbGEgZ3JhbW1haXJlIGRlICoqZ2dwbG90KiouIExlcyBwcmluY2lwYXV4IHZlcmJlcyBzb250IDoKCiAgKiAqKkRhdGEgKGdncGxvdCkqKiA6IGxlcyBkb25uw6llcyBhdSBmb3JtYXQgKipkYXRhZnJhbWUqKiBvdSAqKnRpYmJsZSoqCiAgKiAqKkFlc3RoZXRpY3MgKGFlcykqKiA6IHBvdXIgc8OpcGVjaWZpZXIgbGVzIHZhcmlhYmxlcyDDoCByZXByw6lzZW50ZXIgZGFucyBsZSBncmFwaGUuCiAgKiAqKkdlb21ldHJpY3MgKGdlb21fLi4uKSoqIDogbGUgdHlwZSBkZSBncmFwaGUuCiAgKiAqKlN0YXRpc3RpY3MgKHN0YXRfLi4uKSoqIDogdXRpbGUgcG91ciBzcMOpY2lmaWVyIGRlcyB0cmFuc2Zvcm1hdGlvbnMgZGVzIGRvbm7DqWVzIG7DqWNlc3NhaXJlcyBwb3VyIG9idGVuaXIgbGUgZ3JhcGhlLgogICogKipTY2FsZXMgKHNjYWxlXy4uLikqKiA6IHBvdXIgY29udHJvbGVyIGxlcyBwYXJhbcOodHJlcyBwZXJtZXR0YW50IGQnYWZmaW5lciBsZSBncmFwaGUgKGNoYW5nZW1lbnQgZGUgY291bGV1cnMsIHBhcmFtw6h0cmVzIGRlcyBheGVzLi4uKS4KVG91cyBjZXMgw6lsw6ltZW50cyBzb250IHJlbGnDqXMgYXZlYyBsZSBzeW1ib2xlICoqKyoqLgoKIyMjIGBEYXRhIGV0IGFlc3RoZXRpY3NgCgpDZXMgZGV1eCDDqWzDqW1lbnRzIHJlbnNlaWduZW50IGxlcyBkb25uw6llcyBldCBsZXMgdmFyaWFibGVzIMOgIHJlcHLDqXNlbnRlci4gUGFyIGV4ZW1wbGUsIHBvdXIgbGUgbnVhZ2UgZGUgcG9pbnRzICoqcHJpY2UgdnMgY2FyYXQqKiBvbiBmZXJhCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkKYGBgCgpPbiBwZXV0IGF1c3NpIHV0aWxpc2VyIGxlcyBhcmd1bWVudHMgKipjb2xvcioqLCAqKnNpemUqKiwgKipmaWxsKiogZGFucyBsYSBmb25jdGlvbiAqKmFlcyoqIGxvcnNxdWUgZGVzIGNvdWxldXJzIG91IGRlcyB0YWlsbGVzIHNvbnQgZMOpZmluaWVzIHBhciBkZXMgdmFyaWFibGVzIGR1IGpldSBkZSBkb25uw6llcy4gUGFyIGV4ZW1wbGUKCmBgYHtyIGV2YWw9VH0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSxjb2xvcj1jdXQpCmBgYAoKIyMjIGBHZW9tZXRyaWNzYAoKVXRpbGUgcG91ciBkw6ljcmlyZSBsZSB0eXBlIGRlIHJlcHLDqXNlbnRhdGlvbnMuIFBvdXIgdW4gbnVhZ2UgZGUgcG9pbnRzLCBvbiB1dGlsaXNlcmEgcGFyIGV4ZW1wbGUgKipnZW9tX3BvaW50KiogOgoKYGBge3J9CmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UsY29sb3I9Y3V0KStnZW9tX3BvaW50KCkKYGBgCgpPbiBvYnNlcnZlIHF1ZSAqKmdncGxvdCoqIGFqb3V0ZSBsYSBsw6lnZW5kZSBhdXRvbWF0aXF1ZW1lbnQuIFZvaWNpIHF1ZWxxdWVzIGV4ZW1wbGVzIGRlICoqZ2VvbWV0cmljcyoqIDoKCkdlb20gICB8IERlc2NyaXB0aW9ufCBBZXN0aGV0aWNzIAotLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLQpnZW9tX3BvaW50KCl8IG51YWdlIGRlIHBvaW50cyB8IHgsIHksIHNoYXBlLCBmaWxsIApnZW9tX2xpbmUoKXwgIExpZ25lIChvcmRvbm7DqWUgc2Vsb24geCkgfCB4LCB5LCBsaW5ldHlwZQpnZW9tX2FibGluZSgpfCAgTGlnbmUgfCBzbG9wZSwgaW50ZXJjZXB0IApnZW9tX3BhdGgoKSB8IExpZ25lIChvcmRvbm7DqWUgcGFyIGwnaW5kZXgpIHwgeCwgeSwgbGluZXR5cGUgCmdlb21fdGV4dCgpIHwgVGV4dGUgfCB4LCB5LCBsYWJlbCwgaGp1c3QsIHZqdXN0IApnZW9tX3JlY3QoKSB8IFJlY3RhbmdsZSB8IHhtaW4sIHhtYXgsIHltaW4sIHltYXgsIGZpbGwsIGxpbmV0eXBlCmdlb21fcG9seWdvbigpIHwgUG9seWdvbmUgfCB4LCB5LCBmaWxsLCBsaW5ldHlwZQpnZW9tX3NlZ21lbnQoKSB8IFNlZ21lbnQgfCB4LCB5LCBmaWxsLCBsaW5ldHlwZSAKZ2VvbV9iYXIoKSB8IERpYWdncmFtbWUgZW4gYmFycmVzIHwgeCwgZmlsbCwgbGluZXR5cGUsIHdlaWdodCAKZ2VvbV9oaXN0b2dyYW0oKSB8IEhpc3RvZ3JhbW1lIHwgeCwgZmlsbCwgbGluZXR5cGUsIHdlaWdodCAKZ2VvbV9ib3hwbG90KCkgfCBCb3hwbG90IHwgeCwgeSwgZmlsbCwgd2VpZ2h0IApnZW9tX2RlbnNpdHkoKSB8IERlbnNpdMOpIHwgeCwgeSwgZmlsbCwgbGluZXR5cGUgCmdlb21fY29udG91cigpIHwgTGlnbmVzIGRlIGNvbnRvdXIgfCB4LCB5LCBmaWxsLCBsaW5ldHlwZSAKZ2VvbV9zbW9vdGgoKSB8IExpc3NldXIgKGxpbsOpYWlyZSBvdSBub24gbGluw6lhaXJlKSB8IHgsIHksIGZpbGwsIGxpbmV0eXBlIApUb3VzIHwgfCBjb2xvciwgc2l6ZSwgZ3JvdXAKCgojIyMgRXhlcmNpY2UgNgoKKiBUcmFjZXIgbGUgZGlhZ3JhbW1lIGVuIGJhcnJlcyBkZSBsYSB2YXJpYWJsZSAqKmN1dCoqIChhdmVjIGRlcyBiYXJyZXMgYmxldXMpLgoqIFRyYWNlciBsZSBkaWFncmFtbWUgZW4gYmFycmVzIGRlIGxhIHZhcmlhYmxlICoqY3V0KiogKGF2ZWMgdW5lIGNvdWxldXIgcG91ciBjaGFxdWUgbW9kYWxpdMOpIGRlICoqY3V0KiopLgoKCgojIyMgYFN0YXRpc3RpY3NgIChwYXJ0aWUgb3B0aW9ubmVsbGUpCgpDZXJ0YWlucyBncmFwaGVzIG7DqWNlc3NpdGVudCBkZXMgY2FsY3VscyBkJ2luZGljYXRldXJzIHN0YXRpc3RpcXVlcywgY29tbWUgcGFyIGV4ZW1wbGUgbGUgZGlhZ2FtbWUgZW4gYmFycmVzIG91IGwnaGlzdG9ncmFtbWUgb8O5IGlsIGZhdXQgY2FsY3VsZXIgZGVzIGhhdXRldXJzLiBMZXMgdHJhbnNmb3JtYXRpb25zIHNpbXBsZXMgcGV1dmVudCBzZSBmYWlyZSByYXBpZGVtZW50LCBvbiBwZXV0IHBhciBleGVtcGxlIHRyYWNlciBsYSBmb25jdGlvbiAqKnNpbnVzKiogYXZlYwoKYGBge3J9CkQgPC0gZGF0YS5mcmFtZShYPXNlcSgtMipwaSwyKnBpLGJ5PTAuMDEpKQpnZ3Bsb3QoRCkrYWVzKHg9WCx5PXNpbihYKSkrZ2VvbV9saW5lKCkKYGBgCgpMYSB0cmFuc2Zvcm1hdGlvbiBlc3QgcHLDqWNpc8OpZSBkYW5zIGxhIGZvbmN0aW9uICoqYWVzKiouIFBvdXIgZGVzIHRyYW5zZm9ybWF0aW9ucyBwbHVzIGNvbXBsZXhlcywgbm91cyBkZXZvbnMgdXRpbGlzZXIgZGVzICoqc3RhdGlzdGljcyoqLiBVbmUgZm9uY3Rpb24gKipzdGF0KiogcGVybWV0IGRlIGTDqWZpbmlyIGRlcyBub3V2ZWxsZXMgdmFyaWFibGVzIMOgIHBhcnRpciBkdSBqZXUgZGUgZG9ubsOpZXMgaW5pdGlhbCwgaWwgZXN0IGVuc3VpdGUgcG9zc2libGUgZGUgcmVwcsOpc2VudGVyIGNlcyBub3V2ZWxsZXMgdmFyaWFibGVzLgpQYXIgZXhlbXBsZSwgbGEgZm9uY3Rpb24gKipzdGF0X2JpbioqLCBxdWkgZXN0IHV0aWxpc8OpZSBwYXIgZMOpZmF1dCBwb3VyIGNvbnN0cnVpcmUgZGVzIGhpc3RvZ3JhbW1lcywgcHJvZHVpdCBsZXMgdmFyaWFibGVzIHN1aXZhbnRlcyA6CgoqIGBjb3VudGAsIGxlIG5vbWJyZSBkJ29ic2VydmF0aW9ucyBkYW5zIGNoYXF1ZSBjbGFzc2VzLgoqIGBkZW5zaXR5YCwgbGEgdmFsZXVyIGRlIGxhIGRlbnNpdMOpIGRlcyBvYnNlcnZhdGlvbnMgZGFucyBjaGFxdWUgY2xhc3NlIChmcsOpcXVhbmNlIGRpdmlzw6llIHBhciBsYXJnZXVyIGRlIGxhIGNsYXNzZSkuCiogYHhgLCBsZSBjZW50cmUgZGUgbGEgY2xhc3NlLgoKUGFyIGTDqWZhdXQgKmdlb21faGlzdG9ncmFtKiByZXByw6lzZW50ZSBzdXIgbCdheGUgJHkkIGxlIG5vbWJyZSBkJ29ic2VydmF0aW9ucyBkYW5zIGNoYXF1ZSBjbGFzc2UgKGxhIHZhcmlhYmxlICoqY291bnQqKikuIAoKYGBge3J9CmdncGxvdChkaWFtb25kcykrYWVzKHg9cHJpY2UpK2dlb21faGlzdG9ncmFtKGJpbnM9NDApCmBgYAoKU2kgb24gc291aGFpdGUgdmlzdWFsaXNlciBsYSBkZW5zaXTDqSwgaWwgZmF1ZHJhIHV0aWxpc2VyCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMpK2Flcyh4PXByaWNlLHk9Li5kZW5zaXR5Li4pK2dlb21faGlzdG9ncmFtKGJpbnM9NDApCmBgYAoKSWwgZXN0IHBvc3NpYmxlIGQndXRpbGlzZXIgbGVzIGZvbmN0aW9ucyAqKnN0YXRfKiogw6AgbGEgcGxhY2UgZGVzICoqZ2VvbV8qKiBwb3VyIGNlcnRhaW5lcyByZXByw6lzZW50YXRpb25zLiBDaGFxdWUgZm9uY3Rpb24gKipzdGF0XyoqIHBvc3PDqGRlIHBhciBkw6lmYXV0IHVuICoqZ2VvbV8qKiBldCByw6ljaXByb3F1ZW1lbnQuIFBhciBleGVtcGxlLApgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpCmdncGxvdChkaWFtb25kczIpK2Flcyh4PWNhcmF0LHk9cHJpY2UpK3N0YXRfc21vb3RoKG1ldGhvZD0ibG9lc3MiKQpgYGAKcHJvZHVpc2VudCBsZSBtw6ptZSBncmFwaGUuIE9uIHBldXQgY2hhbmdlciBjZXJ0YWluZXMgb3B0aW9ucyBncmFwaGlxdWVzIGRhbnMgbGVzIGZvbmN0aW9ucyAqKnN0YXRfKiogZW4gdXRpbGlzYW50IGwnb3B0aW9uICoqZ2VvbSoqIDoKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlKStzdGF0X3Ntb290aChtZXRob2Q9ImxvZXNzIixnZW9tPSJwb2ludCIpCmBgYAoKVm9pY2kgcXVlbHF1ZXMgZXhlbXBsZSBkZSBmb25jdGlvbnMgKipzdGF0XyoqCgogU3RhdCAgIHwgIERlc2NyaXB0aW9uIHwgIFBhcmFtw6h0cmVzCi0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tCnN0YXRfaWRlbnRpdHkoKSB8IGF1Y3VuZSB0cmFuc2Zvcm1hdGlvbiB8ICAKc3RhdF9iaW4oKSB8IENvdW50IHwgYmlud2lkdGgsIG9yaWdpbiAKc3RhdF9kZW5zaXR5KCkgfCBEZW5zaXR5IHwgYWRqdXN0LCBrZXJuZWwgCnN0YXRfc21vb3RoKCkgfCBTbW9vdGhlciB8IG1ldGhvZCwgc2UgCnN0YXRfYm94cGxvdCgpIHwgQm94cGxvdCB8IGNvZWYgCgoqc3RhdCogZXQgKmdlb20qIG5lIHNvbnQgcGFzIHRvdWpvdXJzIHNpbXBsZXMgw6AgY29tYmluZXIuIE5vdXMgcmVjb21tYW5kb25zIGQndXRpbGlzZXIgKipnZW9tKiogbG9yc3F1J29uIGTDqWJ1dGUgYXZlYyAqKmdncGxvdCoqLgoKIyMjIEV4ZXJjaWNlIDcgKG9wdGlvbmFsKQoKT24gY29uc2lkw6hyZSB1bmUgdmFyaWFibGUgcXVhbGl0YXRpdmUgJFgkIGRvbnQgbGEgbG9pIGVzdCBkb25uw6llIHBhcgokJFAoWD1yZWQpPTAuMyxcIFAoWD1ibHVlKT0wLjIsXCBQKFg9Z3JlZW4pPTAuNCxcIFAoWD1ibGFjayk9MC4xJCQKVHJhY2VyIGxlIGRpZ3JhbW1lIGVuIGJhcnJlcyBhc3NvY2nDqSDDoCBjZXR0ZSBkaXN0cmlidXRpb24uCgoKIyMjIGBTY2FsZXNgCgpMZXMgw6ljaGVsbGVzICgqKnNjYWxlcyoqKSBjb250cm9sZW50IGNlcnRhaW5lcyBvcHRpb25zIHBvdXIgbGVzIHZhcmlhYmxlcyB1dGlsaXPDqWVzIGRhbnMgKiphZXMqKiAoY2hhbmdlbWVudCBkZSBjb3VsZXVycywgZGUgdGFpbGxlcy4uLikuIE9uIHV0aWxpc2UgZ8OpbsOpcmFsZW1lbnQgY2UgdmVyYmUgw6AgbGEgZGVybmnDqHJlIMOpdGFwZSBkZSBjb25zdHJ1Y3Rpb24gZHUgZ3JhcGhlLiBMYSBzeW50YXhlIGVzdCBkw6lmaW5pZSBjb21tZSBzdWl0IDoKCiogZMOpYnV0ICoqc2NhbGVfKiouCiogYWpvdXQgZGUgbCdhZXN0aGV0aWNzIHF1ZSBsJ29uIHNvdWhhaXRlIG1vZGlmaWVyICgqKmNvbG9yKiosICoqZmlsbCoqLCAqKnhfKiopLgoqIGZpbiA6IG5vbSBkZSBsJ8OpY2hlbGxlICgqKm1hbnVhbCoqLCAqKmlkZW50aXR5KiouLi4pCgpQYXIgZXhlbXBsZSwKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlLGNvbG9yPWN1dCkrZ2VvbV9wb2ludCgpKwpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIkZhaXIiPSJibGFjayIsIkdvb2QiPSJ5ZWxsb3ciLAoiVmVyeSBHb29kIj0iYmx1ZSIsIlByZW1pdW0iPSJyZWQiLCJJZGVhbCI9ImdyZWVuIikpCmBgYAoKVm9pY2kgcXVlbHF1ZXMgZXhlbXBsZXMgZGVzIHByaW5jaXBhbGVzIMOpY2hlbGxlcyA6CgphZXMgfCBEaXNjcmV0IHwgQ29udGludQotLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0KQ291bGV1ciAoY29sb3IgZXQgZmlsbCkgfCBicmV3ZXIgfCBncmFkaWVudCAKICAtICB8IGdyZXkgfCBncmFkaWVudDIgCiAgLSAgfCBodWUgfCBncmFkaWVudG4gCiAtIHwgaWRlbnRpdHkgfCAKIC0gfCBtYW51YWwgfApQb3NpdGlvbiAoeCBldCB5KSB8IGRpc2NyZXRlIHwgY29udGlub3VzIAotIHwgfCBkYXRlIApGb3JtZSB8IHNoYXBlIHwgCi0gfCBpZGVudGl0eSB8IAotIHwgbWFudWFsIHwgClRhaWxsZSAgfCBpZGVudGl0eSB8IHNpemUgCi0gfCBtYW51YWwgfCAKCk5vdXMgcHLDqXNlbnRvbnMgbWFpbnRlbmFudCBxdWVscXVlcyBleGVtcGxlcyA6CgoqIGBDb3VsZXVyIGRhbnMgdW4gZGlhZ3JhbW1lIGVuIGJhcnJlc2AKCmBgYHtyfQpwMSA8LSBnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jdXQpK2dlb21fYmFyKGFlcyhmaWxsPWN1dCkpCnAxCmBgYAoKT24gY2hhbmdlIGxhIGNvdWxldXIgZW4gdXRpbGlzYW50IGxhIHBhbGV0dGUgKipQdXJwbGVzKiogOgoKYGBge3J9CnAxK3NjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlB1cnBsZXMiKQpgYGAKCgoqIGBHcmFkaWVudCBkZSBjb3VsZXVycyBwb3VyIHVuIG51YWdlIGRlIHBvaW50c2AgOgpgYGB7cn0KcDIgPC0gZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9wb2ludChhZXMoY29sb3I9ZGVwdGgpKQpwMgpgYGAKCk9uIGNoYW5nZSBsZSBncmFkaWVudCBkZSBjb3VsZXVyCmBgYHtyfQpwMitzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InJlZCIsaGlnaD0ieWVsbG93IikKYGBgCgoqICBgTW9kaWZpY2F0aW9uIHN1ciBsZXMgYXhlc2AKCmBgYHtyfQpwMitzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLjUsMyxieT0wLjUpKStzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0icHJpeCIpK3NjYWxlX2NvbG9yX2dyYWRpZW50KCJQcm9mb25kZXVyIikKYGBgCgoKIyMjIGBHcm91cCBldCBmYWNldHNgCgoqKmdncGxvdCoqIHBlcm1ldCBkZSBmYWlyZSBkZXMgcmVwcsOpc2VudGF0aW9ucyBwb3VyIGRlcyBncm91cGVzIGQnaW5kaXZpZHVzLiBJbCBlc3QgcG9zc2libGUgZGUgcHJvY8OpZGVyIGRlIGRldXggZmHDp29ucyBkaWZmw6lyZW50ZXMgOgoKKiByZXByw6lzZW50ZXIgZGVzIHNvdXMgZ3JvdXBlcyBzdXIgbGUgbcOqbWUgZ3JhcGhlLCBvbiB1dGlsaXNlIGwnb3B0aW9uICpncm91cCogZGFucyAqKmFlcyoqIDsKKiByZXByw6lzZW50ZXIgZGVzIHNvdXMgZ3JvdXBlcyBzdXIgZGVzIGdyYXBoZXMgZGlmZsOpcmVudHMsIG9uIHV0aWxpc2UgbGUgdmVyYmUgKipmYWNldHMqKi4KClJlcHLDqXNlbnRvbnMgaWNpIChzdXIgbGUgbcOqbWUgZ3JhcGhlKSBsZSBsaXNzZXVyICoqcHJpY2UgdnMgY2FyYXQqKiBwb3VyIGNoYXF1ZSBtb2RhbGl0w6kgZGUgKmN1dCogIApgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSxncm91cD1jdXQsY29sb3I9Y3V0KStnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIikKYGBgCgpQb3VyIG9idGVuaXIgY2V0dGUgcmVwcsOpc2VudGF0aW9uIHN1ciBwbHVzaWV1cnMgZmVuw6p0cmVzLCBvbiB1dGlsaXNlCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpK2ZhY2V0X3dyYXAofmN1dCkKZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpK2ZhY2V0X3dyYXAofmN1dCxucm93PTEpCmBgYAoKKmZhY2V0X2dyaWQqIGV0ICpmYWNldF93cmFwKiBmb250IGRlcyBjaG9zZXMgcHJvY2hlcyBtYWlzIGRpdmlzZW50IGxhIGZlbsOqdHJlIGRlIGZhw6dvbiBkaWZmw6lyZW50ZSA6CgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9wb2ludCgpK2dlb21fc21vb3RoKG1ldGhvZD0ibG0iKStmYWNldF9ncmlkKGNvbG9yfmN1dCkKZ2dwbG90KGRpYW1vbmRzMikrYWVzKHg9Y2FyYXQseT1wcmljZSkrZ2VvbV9wb2ludCgpK2dlb21fc21vb3RoKG1ldGhvZD0ibG0iKStmYWNldF93cmFwKGNvbG9yfmN1dCkKYGBgCgoKIyMgMi4yIENvbXBsw6ltZW50cwoKTGEgc3ludGF4ZSAqKmdncGxvdCoqIGVzdCBkw6lmaW5pZSBzZWxvbiBsZSBzY2jDqW1hIDoKCmdncGxvdCgpK2FlcygpK2dlb21fKCkrc2NhbGVfKCkKCkVsbGUgZXN0IHRyw6hzIGZsZXhpYmxlLCBvbiBwZXV0IHBhciBleGVtcGxlIHNww6ljaWZpZXIgbGVzICoqYWVzKiogZGFucyBsZXMgdmVyYmVzICoqZ2dwbG90Kiogb3UgKipnZW9tXyoqIDoKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlKStnZW9tX3BvaW50KCkKZ2dwbG90KGRpYW1vbmRzMixhZXMoeD1jYXJhdCx5PXByaWNlKSkrZ2VvbV9wb2ludCgpCmdncGxvdChkaWFtb25kczIpK2dlb21fcG9pbnQoYWVzKHg9Y2FyYXQseT1wcmljZSkpCmBgYAoKQ2VjaSBwZXV0IHNlIHLDqXbDqWxlciB0csOocyB1dGlsZSBsb3JzcXUnb24gdXRpbGlzZSBkZXMgKiphZXMqKiBkaWZmw6lyZW50cyBkYW5zIGxlcyAqKmdlb21fKiouCgpPbiBwZXV0IGF1c3NpIGNvbnN0cnVpcmUgdW4gZ3JhcGhlIMOgIGwnYWlkZSBkZSBkaWZmw6lyZW50cyBqZXV4IGRlIGRvbm7DqWVzIDoKYGBge3J9ClggPC0gc2VxKC0yKnBpLDIqcGksYnk9MC4wMDEpClkxIDwtIGNvcyhYKQpZMiA8LSBzaW4oWCkKZG9ubmVlczEgPC0gZGF0YS5mcmFtZShYLFkxKQpkb25uZWVzMiA8LSBkYXRhLmZyYW1lKFgsWTIpCmdncGxvdChkb25uZWVzMSkrZ2VvbV9saW5lKGFlcyh4PVgseT1ZMSkpKwpnZW9tX2xpbmUoZGF0YT1kb25uZWVzMixhZXMoeD1YLHk9WTIpLGNvbG9yPSJyZWQiKQpgYGAKCklsIGV4aXN0ZSBkJ2F1dHJlcyBmb25jdGlvbnMgKipnZ3Bsb3QqKiA6CgoqICoqZ2d0aXRsZSoqIHBvdXIgYWpvdXRlciB1biB0aXRyZS4KKiAqKmdnc2F2ZSoqIHBvdXIgc2F1dmVyIHVuIGdyYXBoZS4KKiAqKnRoZW1lXyoqIHBvdXIgY2hhbmdlciBsZSB0aGVtZSBkdSBncmFwaGUuCgpgYGB7cn0KcCA8LSBnZ3Bsb3QoZGlhbW9uZHMyKSthZXMoeD1jYXJhdCx5PXByaWNlLGNvbG9yPWN1dCkrZ2VvbV9wb2ludCgpCnArdGhlbWVfYncoKQpwK3RoZW1lX2NsYXNzaWMoKQpwK3RoZW1lX2dyZXkoKQpwK3RoZW1lX2J3KCkKYGBgCgoKIyMjIEV4ZXJjaWNlIDgKCjEuIFRyYWNlciBsZXMgZm9uY3Rpb25zIHNpbnVzIGV0IGNvc2ludXMuIE9uIHV0aWxpc2VyYSB0b3V0IGQnYWJvcmQgZGV1eCBqZXV4IGRlIGRvbm7DqWVzIDogdW4gcG91ciBsZSBzaW51cywgbCdhdXRyZSBwb3VyIGxlIGNvc2ludXMuCgoKMi4gRmFpcmUgbGEgbcOqbWUgY2hvc2UgYXZlYyB1biBqZXUgZGUgZG9ubsOpZXMgZXQgZGV1eCAqKmdlb21fbGluZSoqLgoKCjMuIEZhaXJlIGxhIG3Dqm1lIGNob3NlIGF2ZWMgdW4gamV1IGRlIGRvbm7DqWVzIGV0IHVuIHNldWwgKipnZW9tX2xpbmUqKi4gT24gcG91cnJhIHV0aWxpc2VyIGxhIGZvbmN0aW9uICoqZ2F0aGVyKiogZHUgKip0aWR5dmVyc2UqKi4KCgo0LiBUcmFjZXIgbGVzIGRldXggZm9uY3Rpb25zIHN1ciBkZXV4IGZlbsOqdHJlcyBncmFwaGlxdWVzICh1dGlsaXNlciAqKmZhY2V0X3dyYXAqKikuCgoKNS4gRmFpcmUgbGEgbcOqbWUgY2hvc2UgYXZlYyAqKmdyaWQuYXJyYW5nZSoqIGR1IHBhY2thZ2UgKipncmlkRXh0cmEqKi4KCgojIyMgRXhlcmNpY2UgOQoKT24gY29uc2lkw6hyZSBsZXMgZG9ubsOpZXMgKiptdGNhcnMqKgpgYGB7cn0KZGF0YShtdGNhcnMpCnN1bW1hcnkobXRjYXJzKQpgYGAKCjEuIFRyYWNlciBsJ2hpc3RvZ3JhbWUgZGUgKiptcGcqKiAob24gZmVyYSB2YXJpZXIgbGUgbm9tYnJlIGRlIGNsYXNzZXMpLgoKCjIuVHJhY2VyIGwnaGlzdG9ncmFtbWUgZGUgbGEgZGVuc2l0w6kuCgoKMy4gVHJhY2VyIGxlIGRpYWdyYW1tZSBlbiBiYXJyZXMgZGUgICoqY3lsKiouCgo0LiBUcmFjZXIgbGUgbnVhZ2UgZGUgcG9pbnRzICoqZGlzcCB2cyBtcGcqKiBlbiB1dGlsaXNhbnQgdW5lIGNvdWxldXIgZGlmZsOpcmVudGUgcG91ciBjaGFxdWUgdmFsZXVyIGRlICoqY3lsKiouCgoKNS4gQWpvdXRlciBsZSBsaXNzZXVyIGxpbsOpYWlyZSBzdXIgbGUgZ3JhcGhlLgoKCgojIyMgRXhlcmNpY2UgMTAKCjEuIEfDqW7DqXJlciB1biDDqWNoYW50aWxsb24gJCh4X2kseV9pKSxpPTEsXGRvdHMsMTAwJCBzZWxvbiBsZSBtb2TDqGxlIGxpbsOpYWlyZQokJFlfaT0zK1hfaStcdmFyZXBzaWxvbl9pJCQKb8O5ICRYX2kkIHNvbnQgaS5pLmQuIGRlIGxvaSB1bmlmb3JtZSBzdXIgJFswLDFdJCBldCAkXHZhcmVwc2lsb25faSQgc29udCBpLmkuZC4gZGUgbG9pIGdhdXNzaWVubmUgJE4oMCwwLjJeMikkICh1dGlsaXNlciAqKnJ1bmlmKiogZXQgKipybm9ybSoqKS4KCgoyLiBUcmFjZXIgbGUgbnVhZ2UgZGUgcG9pbnRzICoqWSB2cyBYKiogZXQgYWpvdXRlciBsZSBsaXNzZXVyIGxpbsOpYWlyZS4KCjMuIFJlcHLDqXNlbnRlciBsZXMgcsOpc2lkdXMgOiBvbiBham91dGVyYSB1bmUgbGlnbmUgdmVydGljYWxlIGVudHJlIGNoYXF1ZSBwb2ludCBldCBsYSBkcm9pdGUgZGUgbGlzc2FnZSAodXRpbGlzZXIgKipnZW9tX3NlZ21lbnQqKikuCgoKCiMjIyBDaGFsbGVuZ2UgKG9wdGlvbikKCk9uIGNvbnNpZMOocmUgbGVzIGRvbm7DqWVzICoqZGlhbW9uZHMqKi4KCjEuIFRyYWNlciBsZXMgZ3JhcGhlcyBzdWl2YW50cyAodXRpbGlzZXIgKipjb29yZF9mbGlwKiogcG91ciBsZSBzZWNvbmQpLgoKIVtdKGNoYWxsZW5nZTEucGRmKQohW10oY2hhbGxlbmdlMi5wZGYpCiFbXShjaGFsbGVuZ2UzLnBkZikKCgoKMi4gQWpvdXRlciBzdXIgbGUgdHJvaXNpw6htZSBncmFwaGUgbGVzIHF1YXJ0aWxlcyBkZSBsYSB2YXJpYWJsZSAqKmNhcmF0KiogcG91ciBjaGFxdWUgdmFsZXVyIGRlICoqY3V0KiouIE9uIHV0aWxpc2VyYSB1bmUgbGlnbmUgdmVydGljYWxlLgoKCgozLiBFbiBkw6lkdWlyZSBsZSBncmFwaGUgc3VpdmFudCAob24gdXRpbGlzZXJhIGxlIHBhY2thZ2UgKipnZ3N0YW5jZSoqKS4KCiFbXShjaGFsbGVuZ2U0LnBkZikKCgoKIyMjIEV4ZXJjaWNlIDExCgpPbiBjb25zaWTDqHJlIGxlcyBkb25uw6llcyB0ZW5uaXMgc3VyIGxlcyB0b3Vybm9pcyBkdSBncmFuZCBjaGVsZW0gcHLDqXNlbnTDqXMgZGFucyBsYSBmaWNoZSBwcsOpY8OpZGVudGUuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpGcmVuY2hPcGVuX21lbl8yMDEzIDwtIHJlYWRfY3N2KCJGcmVuY2hPcGVuLW1lbi0yMDEzLmNzdiIpClJHMjAxMyA8LSBGcmVuY2hPcGVuX21lbl8yMDEzCldJTUIyMDEzIDwtIHJlYWRfY3N2KCJXaW1ibGVkb24tbWVuLTIwMTMuY3N2IikKUkdfV0lNQjIwMTMgPC0gYmluZF9yb3dzKCJSRyI9UkcyMDEzLCJXSU1CIj1XSU1CMjAxMywuaWQ9IlRvdXJuYW1lbnQiKQpgYGAKCgoxLiBUcmFjZXIgZGV1eCBoaXN0b2dyYW1tZXMgKHVuIHBvdXIgUm9sYW5kIEdhcnJvcywgbCdhdXRyZSBwb3VyIHdpbWJsZWRvbikgcG91ciB2aXN1YWxpc2VyIGxhIGRpc3RyaWJ1dGlvbiBkdSBub21icmUgZCdhY2VzIHBhciBtYXRjaC4KCgoyLiBDb21wYXJlciBsZXMgbm9tYnJlcyBkJ2FjZXMgcGFyIG1hdGNoIGVudHJlIFJvbGFuZCBHYXJyb3MgZXQgV2ltYmxlZG9uIMOgIGwnYWlkZSBkJ3VuIGJveHBsb3QuCgoKMy4gQ29tcGFyZXIgbGVzIG5vbWJyZXMgZGUgcG9pbnRzIGpvdcOpcyBhdSBmaWxldCAoKipOUEEuMStOUEEuMioqKSBwYXIgbWF0Y2ggZW50cmUgUm9sYW5kIEdhcnJvcyBhbmQgV2ltYmxlZG9uIGF2ZWMgdW4gYm94cGxvdC4KCjQuIFBvdXIgbGUgdG91cm5vaSBkZSBSb2xhbmQgR2Fycm9zLCDDqXR1ZGllciDDoCBsJ2FpZGUgZCd1biBib3hwbG90IGwnaW5mbHVlbmNlIGR1IHByZW1pZXIgc2VydmljZSBzdXIgbGUgcsOpc3VsdGF0IGR1IG1hdGNoLgoKCjUuIGZhaXJlIGxhIG3Dqm1lIGNob3NlIHBvdXIgV2ltYmxlZG9uLgoKNi4gQ29tcGFyZXIgbGVzIHBvdXJjZW50YWdlcyBkZSBwcmVtaWVyIHNlcnZpY2UgZGVzIHZhaXF1ZXVycyBkZSBtYXRjaCBlbnRyZSBsZXMgdG91cm5vaXMgZGUgUm9sYW5nIEdhcnJvcyBldCBXaW1ibGVkb24uCgo=